package com.yt.server.action;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Collections;
import javax.management.ServiceNotFoundException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import com.yt.server.Context;
import com.yt.server.api.IConnection;
import com.yt.server.api.IScope;
import com.yt.server.api.action.IActionCall;
import com.yt.server.api.action.IActionInvoker;
import com.yt.server.api.action.IActionResolver;
import com.yt.server.exception.MethodNotFoundException;
import com.yt.server.exception.NotAllowedException;

/**
 * 控制器调用者
 * 
 * @author 刘晶
 */
public class ActionInvoker implements IActionInvoker, ApplicationContextAware {

	private static final Logger log = LoggerFactory
			.getLogger(ActionInvoker.class);

	private ApplicationContext ctx = null;

	private IActionResolver[] actionResolvers;

	public void setActionResolvers(IActionResolver[] actionResolvers) {
		this.actionResolvers = actionResolvers;
	}

	@Override
	public void setApplicationContext(ApplicationContext arg0)
			throws BeansException {
		this.ctx = arg0;
	}

	@Override
	public Object getAction(IScope scope, String actionName) {
		Object action = null;
		if (actionName == null || actionName.equals("")) {
			return scope.getHandler();
		}
		for (IActionResolver resolver : actionResolvers) {
			action = resolver.resolveService(scope, actionName);
			if (action != null) {
				return action;
			}
		}
		return null;
	}

	@Override
	public boolean invoke(IActionCall call) {
		return invoke(call, call.getScope(),
				getAction(call.getScope(), call.getActionName()));
	}

	@Override
	public boolean invoke(IActionCall call, IScope scope, Object action) {
		if (action == null) {
			String actionName = call.getActionName();
			call.setException(new ServiceNotFoundException(actionName));
			call.setStatus(IActionCall.STATUS_SERVICE_NOT_FOUND);
			log.warn("action not found: {}", actionName);
			return false;
		} else {
			// 处理Scope数据
			return invoke(call, action);
		}
	}

	public boolean invoke(IActionCall call, Object action) {
		IConnection conn = Context.getConnectionLocal();
		String methodName = call.getActionMethodName();
		if (methodName.charAt(0) == '@') {
			methodName = methodName.substring(1);
		}
		Object[] args = call.getArguments();
		Object[] argsWithConnection;
		if (args != null) {
			argsWithConnection = new Object[args.length + 1];
			argsWithConnection[0] = conn;
			for (int i = 0; i < args.length; i++) {
				argsWithConnection[i + 1] = args[i];
			}
		} else {
			argsWithConnection = new Object[] { conn };
		}
		Object[] methodResult = null;
		methodResult = ServiceUtils.findMethodWithExactParameters(action,
				methodName, argsWithConnection);
		if (methodResult.length == 0 || methodResult[0] == null) {
			methodResult = ServiceUtils.findMethodWithExactParameters(action,
					methodName, args);
			if (methodResult.length == 0 || methodResult[0] == null) {
				methodResult = ServiceUtils.findMethodWithListParameters(
						action, methodName, argsWithConnection);
				if (methodResult.length == 0 || methodResult[0] == null) {
					methodResult = ServiceUtils.findMethodWithListParameters(
							action, methodName, args);
					if (methodResult.length == 0 || methodResult[0] == null) {
						log.error(
								"没有找到匹配参数的Method",
								new Object[] {
										methodName,
										(args == null ? Collections.EMPTY_LIST
												: Arrays.asList(args)), action });
						call.setStatus(IActionCall.STATUS_METHOD_NOT_FOUND);
						if (args != null && args.length > 0) {
							call.setException(new MethodNotFoundException(
									methodName, args));
						} else {
							call.setException(new MethodNotFoundException(
									methodName));
						}
						return false;
					}
				}
			}
		}
		Object result = null;
		Method method = (Method) methodResult[0];
		Object[] params = (Object[]) methodResult[1];

		try {
			log.debug("Invoking method: ", method.toString());
			if (method.getReturnType() == Void.class) {
				method.invoke(action, params);
				call.setStatus(IActionCall.STATUS_SUCCESS_VOID);
			} else {
				result = method.invoke(action, params);
				log.debug("result: {}", result);
				call.setStatus(result == null ? IActionCall.STATUS_SUCCESS_NULL
						: IActionCall.STATUS_SUCCESS_RESULT);
			}
			call.setResult(result);
		} catch (NotAllowedException e) {
			call.setException(e);
			call.setStatus(IActionCall.STATUS_ACCESS_DENIED);
			return false;
		} catch (IllegalAccessException accessEx) {
			call.setException(accessEx);
			call.setStatus(IActionCall.STATUS_ACCESS_DENIED);
			log.error("Error executing call:", call);
			log.error("Service invocation error", accessEx);
			return false;
		} catch (InvocationTargetException invocationEx) {
			call.setException(invocationEx);
			call.setStatus(IActionCall.STATUS_INVOCATION_EXCEPTION);
			return false;
		} catch (Exception ex) {
			call.setException(ex);
			call.setStatus(IActionCall.STATUS_GENERAL_EXCEPTION);
			log.error("Error executing call: ", call);
			log.error("Service invocation error", ex);
			return false;
		}
		return true;
	}

	public static void main(String[] args) {
		// IActionInvoker invoker = new ActionInvoker();
		// Demo demo = new Demo();
		// IConnection connection = new Connection();
		// Context.setConnectionLocal(connection);
		// IActionCall call = new ActionCall("Demo", "demo", new Object[]{}, new
		// Scope("测试"));
		// invoker.invoke(call, demo);
		// System.out.println(call.getResult());
		// System.out.println(call.getException());
	}
}